Variablity

Die Bearbeitung von den Werkstücken soll in unterschiedlichen Fertigungszellen durchgeführt werden können.

Constraints

Context

Die Fertigungszellen können mit unterschiedlichen Maschinen ausgestattet werden. Für diese Maschinen müssen auch leicht unterschiedliche Abläufe für die Bearbeitung vorhanden sein. So kann zum Beispiel für das Beladen und das Entladen der Bearbeitungsmaschinen entweder ein Roboter mit Einfachgreifer oder ein Roboter mit Doppelgreifer benutzt werden. Eine Ablaufsteuerung die für einen Einfachgreifer entwickelt wurde kann zwar auch für einen Doppelgreifer benutzt werden, jedoch wird dann nur ein Greifer des Doppelgreifer genutzt. Um die Doppelgreifer auch voll nutzen zu können, muß die Ablaufsteuerung in der Klasse ProcessingControl geändert werden. Da die Ablaufsteuerung jedoch sehr komplex ist, sind die Änderungen sehr arbeitsaufwendig und fehleranfällig.

Examples

Extension

Dieser Hotspot wurde mit dem Entwurfsmuster Strategy implementiert. Das Vorgehen für eine Erweiterung entspricht deshalb dem Vorgehen für das Strategy-Muster in [Gamma et. al., 1995].

  1. Es muß eine neue Klasse implementiert werden. Diese Klasse muß von der Klasse Strategy abgeleitet werden.
  2. Der Konstruktor muß die Attribute theGripperType, theNumberOfBuffers, staggerType und nStrategyNo initialisieren. Anhand dieser Attribute wird später die Strategy ausgewählt.
  3. In der neuen Klasse muß die virtuelle Methode Execute überschrieben werden. Diese Methode dient zur Anpassung der Ablaufsteuerung an eine spezielle Konfiguration einer Fertigungszelle.
  4. Mit Hilfe von der Methode AppendStrategy wird die neue Strategy der Klasse StrategyCollection bekannt gemacht.

Aufbau einer Fertigungsstrategie [Peters, 1997]

Der Feinablauf der Fertigungssteuerung wird in der Methode Execute formuliert. Diese Methode ist in der Basisklasse rein virtuell, sie muß daher in einer von Strategy abgeleiteten Klasse definiert werden.

In der Methode Execute muß zunächst die Methode StartSignaler der Klasse ProcessingMediator aufgerufen werden. Dieser Aufruf startet den Signalisierungsmechanismus des Mediators. Er sorgt dafür, daß die sogenannten Fertigmeldungssperren, die von den Methoden der Klasse ProcessingMediator vor der Ausführung eines Auftrags belegt werden, nach Abarbeitung des Auftrags automatisch wieder freigegeben werden. Die Fertigmeldungssperren sind immer dann belegt, wenn der Zustand der realen Objekte der Fertigungszelle vom Zustand der zugehörigen Objekte in OSEFA abweicht. Der Programmierer der Strategie braucht daher nicht selber zu gegebener Zeit eine Fertigmeldungssperre wieder freigeben, er muß lediglich am Anfang der Methode Execute diesen Signalisierungsmechanismus starten. Entsprechend muß am Ende der programmierten Strategie die Methode StopSignaler der Klasse ProcessingMediator aufgerufen werden. Dadurch wird der Signalisierungsmechanismus gestoppt und der damit verbundene Thread verbraucht keine weitere Rechenzeit.

Je nachdem, wie der konkrete Fertigungsablauf aussehen soll, können nun die Methoden der Klasse Strategy bzw. der Klasse ProcessingMediator aufgerufen werden. Prinzipiell ist es möglich, Methoden aus beiden Klassen gemischt zu verwenden. Dies ist jedoch nicht ratsam, da die Methoden der Klasse Strategy implizit die logischen Sperren BufferLock und XactionLock belegen und freigeben, die mit den Methoden der Klasse ProcessingMediator direkt manipuliert werden können. Daher ist es ratsam entweder nur Methoden der Klasse ProcessingMediator oder aber nur Methoden der Klasse Strategy zu verwenden.

Im Folgenden wird beispielhaft beschrieben, welche Methoden zur Formulierung einer Standardstrategie zu verwenden sind. Es werden jeweils die benötigten Methoden der Klasse ProcessingMediator und die alternativ zu verwendenden Methoden der Klasse Strategy aufgeführt.

  1. Zu Beginn des Fertigungsablaufs müssen zunächst die benötigten Paletten aus dem Lager in die Pufferlager transportiert werden. Hierzu wird zunächst eine Transaktion geöffnet, die anzeigt, daß die Ablaufsteuerung das betreffende Pufferlager über mehrere Aktionen hinweg sperrt. Dies ist in diesem Fall nötig, da die Überprüfung, ob das Pufferlager leer ist und das daraus resultierende Absetzen eines Kommandos zum Transport einer Palette nicht von anderen Ablaufsteuerungen, die auf den selben Puffer arbeiten, unterbrochen werden darf. Betrachten wir zunächst das Eingangspufferlager so ist hierfür folgender Aufruf nötig:

    theMediatorPtr->Wait( Inbuf, XactionLock );

  2. ( Die korrespondierenden Methoden der Klasse Strategy werden am Ende jeden Absatzes aufgeführt, da sie praktisch mehrere Methoden der Klasse ProcessingMediator zusammenfassen.)
    Anschließend wird das entsprechende Pufferlager mit folgendem Aufruf gesperrt:

    theMediatorPtr->Wait( Inbuf, BufferLock );

  3. Nun kann überprüft werden, ob das Pufferlager leer ist. Dies geschieht mit folgendem Aufruf:

    if (theMediatorPtr->BufferEmpty( Inbuf ))

  4. Bei leerem Pufferlager kann nun eine Palette aus dem Lager geholt werden, ist das Pufferlager nicht leer, so muß der Programmierer der Strategie entscheiden, wie weiter verfahren werden soll. So kann zum Beispiel ein Fehler erzeugt werden oder der Ablauf wird abgebrochen, oder der Fall wird ignoriert und hat keine Auswirkungen. Der Aufruf zum Transport der Palette ins Pufferlager ist:

    theMediatorPtr->GetPallet( Inbuf, WithRaw );

  5. Der zweite Parameter gibt an, welcher Teiletyp auf der Palette vorhanden sein soll. An Hand des aktuellen Maschinenauftrags kann das System damit feststellen, welche Palette zu transportieren ist. WithRaw gibt in diesem Fall an, daß es sich in Bezug auf diesen Fertigungsabschnitt um Rohteile (unbearbeitete Teile) handelt. Durch den Aufruf der Methode GetPallet werden Fertigmeldungssperren belegt, die erst nach Abarbeitung des Kommandos durch das Transportsystem wieder freigegeben werden. Die von den einzelnen Methoden belegten Fertigmeldungssperren sind bei der Dokumentation der Methoden der Klasse ProcessingMediator beschrieben.
    Nun kann die Sperre des Pufferlagers wieder aufgehoben werden:

    theMediatorPtr->Signal( Inbuf, BufferLock );

  6. Die Transaktion wird geschlossen:

    theMediatorPtr->Signal( Inbuf, XactionLock );

  7. Für die Palette im Ausgangspuffer wird in gleicher Weise verfahren. In den Aufrufen ist jeweils Inbuf durch Outbuf zu ersetzen. In den Ausgangspuffer wird eine Palette mit Platz für bearbeitete Teile transportiert (SpaceForMachined).
    Den Transport der Paletten in die Pufferlager kann man mit Hilfe der Methoden der Klasse Strategy auch wie folgt formulieren:

    SetXaction( Inbuf, CloseXaction );
    SetXaction( Outbuf, CloseXaction );
    GetPallet( Inbuf, WithRaw );
    GetPallet( Outbuf, SpaceForMachined );

  8. Die ersten beiden Aufrufe stellen sicher, daß die Transaktionen geschlossen sind, da diese von der Methode GetPallet automatisch geöffnet und wieder geschlossen werden.
  9. Nun kann die eigentliche Teilefertigung programmiert werden. Prinzipiell ist hierzu eine Schleife nötig, die solange ausgeführt wird, solange für diesen Maschinenauftrag noch Teile zu fertigen sind bzw. solange kein Abbruch der Fertigung, z.B. durch einen Fehler, angefordert wurde:

    while( ( theMediatorPtr->PartStillToMachine()>0 ) && ( !(theMediatorPtr->TerminateGiven(ClearUp)) ) )

  10. Innerhalb der Schleife muß nun zunächst die Bearbeitungsmaschine mit einem Teil aus dem Eingangspuffer bestückt werden. Hierzu ist zu beachten, das im letzten, gerade beendeten Schleifendurchgang (wir betrachten hier den "eingeschwungenen" Zustand) möglicherweise ein Austausch der Palette im Eingangspuffer stattgefunden hat. In diesem Fall ist auf diesen Puffer eine Transaktion geöffnet, damit nach dem Tausch der Palette zuerst eine Handhabungsoperation aus dem Puffer erfolgen kann, ohne daß eine andere Ablaufsteuerung dazwischenkommt. (Sonst käme es bei ergänzender Fertigung zu einem ständigen Austausch der Palette, beachten Sie hierzu auch die Überlegungen aus dem Entwurf Ballarin!) Aus diesem Grund wird eine Transaktion auf den Eingangspuffer nur dann geöffnet, wenn sie nicht schon geöffnet wurde. Die Tatsache, ob eine Transaktion geöffnet wurde, muß der Programmierer sich in einer lokale boolschen Variablen, in diesem Beispiel InbufXactionOpen, merken, sofern er die Methoden der Klasse ProcessingMediator verwendet. Die Methoden der Klasse Strategy verwalten diese Information selbstständig.

    if ( InbufXactionOpen == False )
    theMediatorPtr->Wait( Inbuf, XactionLock);
    theMediatorPtr->Wait( Inbuf, BufferLock );

  11. Nun kann die Maschine mit

    theMediatorPtr->Handle( Load, Inbuf );

  12. beladen werden. Die Transaktion auf den Eingangspuffer wird nun geschlossen, dies wird in der lokalen Variablen vermerkt:

    InbufXactionOpen = False;
    theMediatorPtr->Signal( Inbuf, BufferLock );
    theMediatorPtr->Signal( Inbuf, XactionLock );

  13. Nun muß dafür gesorgt werden, daß sich im Ausgangspufferlager eine Palette befindet, welche Platz für das bearbeitete Teil aufweist. Auch hier gilt, daß überprüft werden muß, ob bereits eine Transaktion auf das Ausgangspufferlager geöffnet ist (lokale Variable OutbufXactionOpen):

    if ( OutbufXactionOpen == False )
    theMediatorPtr->Wait( Outbuf, XactionLock );
    theMediatorPtr->Wait( Outbuf, BufferLock );

  14. Jetzt kann überprüft werden, ob ein Austausch der Palette notwendig ist:

    if (theMediatorPtr->PalletFull(Outbuf, PartsMachined))

  15. Ist dies der Fall, so wird mit

    theMediatorPtr->ExchangePallet(Outbuf,SpaceForMachined);

  16. die Palette im Ausgangspufferlager ausgetauscht. Damit sie nicht vor der ersten Handhabungsoperation durch eine konkurrierende Ablaufsteuerung "weggetauscht" werden kann, darf die geöffnete Transaktion nicht geschlossen werden:

    OutbufXactionOpen = True;

  17. Nun kann das Pufferlager wieder freigegeben werden, wenn kein Palettentausch stattgefunden hat, kann die Transaktion geschlossen werden:

    theMediatorPtr->Signal( Outbuf, BufferLock );
    if ( OutbufXactionOpen == False )
    theMediatorPtr->Signal( Outbuf, XactionLock );

  18. Jetzt kann die Fertigung auf der Bearbeitungsmaschine gestartet werden, anschließend wird die Anzahl der gefertigten Teile inkrementiert:

    theMediatorPtr->StartMachining();
    theMediatorPtr->IncrementPartsMachined();

  19. Um das nächste Teil fertigen zu können wird überprüft, ob die Palette im Eingangspuffer ausgewechselt werden muß. Die Vorgehensweise entspricht derjenigen beim Ausgangspuffer:

    if ( InbufXactionOpen == False )
    theMediatorPtr->Wait( Inbuf, XactionLock);
    theMediatorPtr->Wait( Inbuf, BufferLock );
    if (theMediatorPtr->PalletEmpty(Inbuf, RawParts) && (theMediatorPtr->PartsStillToMachine() > 0) )
    {
    theMediatorPtr->ExchangePallet( Inbuf, WithRaw );
    InbuXactionOpen = True;
    }
    theMediatorPtr->Signal( Inbuf, BufferLock );
    if ( InbufXactionOpen == False )
    theMediatorPtr->Signal( Inbuf, XactionLock );

  20. Jetzt wird die Maschine entladen, dazu müssen die schon mehrfach eingesetzten Sperren wieder auf das Ausgangspufferlager angewendet werden:

    if ( OutbufXactionOpen == False )
    theMediatorPtr->Wait( Outbuf, XactionLock );
    theMediatorPtr->Wait( Outbuf, BufferLock );

  21. Das Kommando zum Entladen der Bearbeitungsmaschine lautet:

    theMediatorPtr->Handle( Unload, Outbuf );

  22. Eine möglicherweise geöffnete Transaktion kann nun geschlossen werden:

    OutbufXactionOpen = False;
    theMediatorPtr->Signal( Outbuf, BufferLock );
    theMediatorPtr->Signal( Outbuf, XactionLock );

  23. Damit ist die Schleife zur Teilefertigung beendet.

    Mit den Methoden der Klasse Strategy läßt sich der Inhalt der Schleife auch wesentlich kompakter formulieren. Diese "High-Level"-Routinen bewirken logisch den gleichen Programmablauf, sie entlasten den Programmierer da die Transaktionsverwaltung innerhalb der Methoden erfolgt:
    Zunächst wird die Maschine beladen, eine dabei geöffnete Transaktion wird anschließend geschlossen:

    Handle( Load, Inbuf, CloseXaction );

  24. Nun wird die Palette im Ausgangspufferlager, wenn nötig, ausgetauscht. Nach einem Austausch bleibt die Transaktion geöffnet (Dies entspricht exakt dem Ablauf, der mit den Methoden des Mediators programmiert wurde):

    ExchangePalletConditionally( Outbuf, IfMachinedFull, DontSync, SpaceForMachined, OpenXactionIfExchanged );

  25. Der Start der Bearbeitungsmaschine erfolgt durch:

    theMediatorPtr->StartMachining();

  26. Jetzt wird die Palette im Eingangspufferlager, wenn nötig, ausgetauscht. Nach einem Austausch bleibt die Transaktion bis zur nächsten Handhabungsoperation geöffnet (Dies entspricht exakt dem Ablauf, der mit den Methoden des Mediators programmiert wurde):

    ExchangePalletConditionally( Inbuf, IfRawEmpty, DontSync, WithRaw, OpenXactionIfExchanged );

  27. Zuletzt wird die Bearbeitungsmaschine entladen und die Transaktion des Ausgangspufferlagers geschlossen:

    Handle( Unload, Outbuf, CloseXaction );

  28. Wenn alle Teile des aktuellen Maschinenauftrags gefertigt worden sind, kann eine Terminierung durchgeführt werden. Eine aus dem Ablauf heraus noch offene Transaktion auf den Eingangspuffer wird zunächst geschlossen:

    InbufXactionOpen = False;

  29. Anschließend kann die Palette im Eingangspufferlager zurück ins Lager transportiert werden, es wird vorher überprüft, ob sich im Puffer auch tatsächlich eine Palette befindet. Die Überprüfung und der anschließende Transport werden innerhalb einer Transaktion durchgeführt. Damit ergibt sich folgender Code:

    theMediatorPtr->Wait( Inbuf, XactionLock );
    theMediatorPtr->Wait( Inbuf, BufferLock );
    if (!(theMediatorPtr->BufferEmpty( Inbuf )))
    theMediatorPtr->StorePallet( Inbuf );
    theMediatorPtr->Signal( Inbuf, BufferLock );
    theMediatorPtr->Signal( Inbuf, Xactio
    nLock );

  30. Befindet sich im Puffer keine Palette mehr, so liegt möglicherweise ein Fehler vor, auf den entsprechend zu reagieren ist.
    Für die Palette im Ausgangspuffer wird in gleicher Weise verfahren. In den Aufrufen ist jeweils Inbuf durch Outbuf zu ersetzen.
    Die entsprechenden Methoden der Klasse Strategy lauten:

    SetXaction( Inbuf, CloseXaction );
    StorePalletIfAny( Inbuf );

  31. Am Schluß muß noch die Methode StopSignaler der Klasse ProcessingMediator aufgerufen werden, um den Signalisierungsmechanismus zu stoppen.

Aufnahme einer neuen Strategie ins System [Peters, 1997]

Zur Aufnahme einer neuen Strategie in den Baukasten OSEFA, muß man zunächst eine neue Klasse von der abstrakten Basisklasse Strategy ableiten. Im folgenden wird eine neue Strategie NewStrategy angelegt:

class NewStrategy : public Strategy
{
public:
NewStrategy( void );
void Execute( void );
};

In der Klasse wird zunächst ein parameterloser Konstruktor definiert. Dieser wird benötigt, um einige Attribute der Basisklasse, abhängig von der Art der neuen Strategie, auf bestimmte Werte zu setzen. Das Attribut theGripperType bestimmt, ob mit Einfach- oder mit Doppelgreifer gearbeitet wird. Das Attribut theNumberOfBuffers gibt die Anzahl der verwendeten Pufferlager an. Das Attribut staggerType gibt an, ob es sich um versetzten Betrieb handelt oder nicht. (siehe hierzu auch die Beschreibung der Attribute der Klasse Strategy im Kapitel 3.6.4).

Diese drei Attribute dienen OSEFA dazu, abhängig von einem Maschinenauftrag, die richtige Strategie auszuwählen. Um es zu ermöglichen mehrere Strategien im System zu halten, die bezüglich dieser drei Attribute die gleichen Vorrausetzungen erfüllen, wurde noch eine optionale Strategienummer nStrategyNo eingeführt. Wenn diese Nummer einen Wert ungleich Null aufweist, so wird bei der Auswahl der Strategie eine Strategie mit dieser Nummer gewählt.

Zur Definition einer neuen Strategie zur Arbeit mit Einfachgreifer, zur Verwendung von zwei Pufferlagern und für nicht versetzten Betrieb würde der Konstruktor also wie folgt aussehen:

NewStrategy::NewStrategy( void )
{
theGripperType = SingleGripper;
theNumberOfBuffers = 2;
staggerType = NotStaggered;
nStrategyNo = 0;
}

Sollte bereits eine Strategie für diesen Einsatzzweck im System vorhanden sein, so muß eine Nummer für die Strategie vergeben und dem Attribut nStrategyNo zugewiesen werden.

Nun folgt die Implementation der Methode Execute deren Aufbau im Kapitel 3.6.2 beschrieben ist.

Damit ist einen neue Strategie fertiggestellt. Zur Verwendung muß sie nun nur noch dem System bekanntgemacht werden, damit dieses die neue Strategie bei einer Strategieauswahl berücksichtigen kann.

Alle im System enthaltenen Strategien werden von der Klasse StrategyCollection verwaltet. Ein Objekt dieser Klasse wird beim Programmstart erzeugt. Von jeder im System enthaltenen Strategieklasse muß ein Objekt instanziiert werden. Ein Zeiger auf dieses Objekt wird dann über die Methode AppendStrategy in der Klasse StrategyCollection eingetragen.

Beispiel Klasse



OSEFA: © '96 Fachhochschule Konstanz, Fachbereich Informatik, Prof. Schmid
Dokumentation: © '97 Universität-GH Siegen, FB 12, Markus Blachnik & Georg Odenthal